#ifndef _DOWNLOADFILE
#define _DOWNLOADFILE
// HttpPost.cpp written by l_zhaohui@163.com
 // 2007/11/30
 #include "stdafx.h"
 #include <windows.h>
 #include <stdio.h>
 #include <stdlib.h>
 
 #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS
 #include <atlbase.h>
 #include <atlstr.h>

#include <string>
using namespace std;
 
#include "../../../Components/Common/LibCommon/Thread.h"
#include "../../../ThirdParty/libcurl/include/curl/curl.h"
#include "../../../Components/Common/LibCommon/util.h"

 #define USE_WINHTTP    //Comment this line to user wininet.
 #ifdef USE_WINHTTP
     #include <winhttp.h>
     #pragma comment(lib, "winhttp.lib")
 #else
     #include <wininet.h>
     #pragma comment(lib, "wininet.lib")
 #endif
 #define BUF_SIZE    (1024)
 
 // CrackedUrl
 class CrackedUrl {
     int m_scheme;
     CStringW m_host;
     int m_port;
     CStringW m_path;
 public:
     CrackedUrl(LPCWSTR url)
     {
         URL_COMPONENTS uc = { 0};
         uc.dwStructSize = sizeof(uc);
 
         const DWORD BUF_LEN = 256;
 
         WCHAR host[BUF_LEN];
         uc.lpszHostName = host;
         uc.dwHostNameLength = BUF_LEN;
 
         WCHAR path[BUF_LEN];
         uc.lpszUrlPath = path;
         uc.dwUrlPathLength = BUF_LEN;
 
         WCHAR extra[BUF_LEN];
         uc.lpszExtraInfo = extra;
         uc.dwExtraInfoLength = BUF_LEN;
 
 #ifdef USE_WINHTTP
         if (!WinHttpCrackUrl(url, 0, ICU_ESCAPE, &uc)) {
             printf("Error:WinHttpCrackUrl failed!\n");
         }
 
 #else
         if (!InternetCrackUrl(url, 0, ICU_ESCAPE, &uc)) {
             printf("Error:InternetCrackUrl failed!\n");
         }
 #endif
         m_scheme = uc.nScheme;
         m_host = host;
         m_port = uc.nPort;
         m_path = path;
     }
 
     int GetScheme() const
     {
         return m_scheme;
     }
 
     LPCWSTR GetHostName() const
     {
         return m_host;
     }
 
     int GetPort() const
     {
         return m_port;
     }
 
     LPCWSTR GetPath() const
     {
         return m_path;
     }
 
     static CStringA UrlEncode(const char* p)
     {
         if (p == 0) {
            return CStringA();
         }
 
         CStringA buf;
 
         for (;;) {
             int ch = (BYTE) (*(p++));
             if (ch == '\0') {
                 break;
             }
 
             if (isalnum(ch) || ch == '_' || ch == '-' || ch == '.') {
                 buf += (char)ch;
            }
            else if (ch == ' ') {
                buf += '+';
            }
            else {
                char c[16];
                wsprintfA(c, "%%%02X", ch);
                buf += c;
            }
        }

        return buf;
    }
};

// CrackedUrl
HINTERNET OpenSession(LPCWSTR userAgent = 0);

HINTERNET Connect(HINTERNET hSession, LPCWSTR serverAddr, int portNo);

HINTERNET OpenRequest(HINTERNET hConnect, LPCWSTR verb, LPCWSTR objectName, int scheme);

BOOL AddRequestHeaders(HINTERNET hRequest, LPCWSTR header);

BOOL SendRequest(HINTERNET hRequest, const void* body, DWORD size);
BOOL EndRequest(HINTERNET hRequest);

BOOL QueryInfo(HINTERNET hRequest, int queryId, char* szBuf, DWORD* pdwSize);

BOOL ReadData(HINTERNET hRequest, void* buffer, DWORD length, DWORD* cbRead);

void CloseInternetHandle(HINTERNET hInternet);

class IDownloadNotify
{
public:
	virtual void NotifyEnd(wstring URL) = 0;
};

#define HTTPException(x,y) y
class IDownload
{
public:
	virtual int DownloadFile(wstring URL,wstring SavePath,wstring Header = _T("")) = 0;
};

class DownloadHelper :public IDownload//,
					 	//public tThreadCtrl
{
public:
	DownloadHelper(IDownloadNotify* Notify)
	{
		m_Notify = Notify;
		m_hMutex = CreateMutex(NULL, FALSE, NULL);

	}
	HANDLE m_hMutex;
	~DownloadHelper()
	{
		CloseHandle(m_hMutex);
	}
	virtual int DownloadFile(wstring URL,wstring SavePath,wstring Header)
	{
		AUTO_MUTEX lock(m_hMutex);
		m_URL = URL;
		m_SavePath = SavePath;
		m_Header = Header;
		//StartThread();
		_beginthread(StaticThreadProc, 0, this);
		return 0;
	}
	static void StaticThreadProc(void* param)
	{
		DownloadHelper* Helper = (DownloadHelper*)(param);
		Helper->Download();
	}
	static size_t ReceiveHTTPData( void *ptr, size_t size, size_t nmemb, void *stream)
	{
		FILE* fp = (FILE*)stream;
		if (fp)
			fwrite(ptr,size,nmemb,fp);
		return size*nmemb;
	}
	int Download()
	{
		FILE* fp = NULL;
		std::wstring wURL;
		std::wstring Header;
		{
			AUTO_MUTEX lock(m_hMutex);
			fp = fopen(WtoUFT8(m_SavePath).c_str(),"w+b");

			wURL = m_URL;
			Header = m_Header;
		}
		if (fp == NULL)
			return -1;

		char m_CURLERR[CURL_ERROR_SIZE];
		CURL* m_pCURL = curl_easy_init();
		// Set common used options.
		CURLcode code;
		try
		{
			// Set error string.
			code = curl_easy_setopt(m_pCURL, CURLOPT_ERRORBUFFER, m_CURLERR);
			if (code != CURLE_OK)
				throw HTTPException(HTTP_INIT_FAIL, code);
			// automatically redirect.
			code = curl_easy_setopt(m_pCURL, CURLOPT_FOLLOWLOCATION, 1);
			if (code != CURLE_OK)
				throw HTTPException(HTTP_INIT_FAIL, code);
			// set data recevier callback.
			code = curl_easy_setopt(m_pCURL, CURLOPT_WRITEFUNCTION, ReceiveHTTPData);
			if (code != CURLE_OK)
				throw HTTPException(HTTP_INIT_FAIL, code);
			// set context for write function.
			code = curl_easy_setopt(m_pCURL, CURLOPT_WRITEDATA, fp);
			if (code != CURLE_OK)
				throw HTTPException(HTTP_INIT_FAIL, code);	
			// set timeout.
			code = curl_easy_setopt(m_pCURL, CURLOPT_CONNECTTIMEOUT, 20);
			if (code != CURLE_OK)
				throw HTTPException(HTTP_INIT_FAIL, code);
			// set default user agent.
			//code = curl_easy_setopt(m_pCURL, CURLOPT_USERAGENT, m_UserAgent.c_str());
			//if (code != CURLE_OK)
			//	throw HTTPException(HTTP_INIT_FAIL, code);
			// set abort condition.
			code = curl_easy_setopt(m_pCURL, CURLOPT_LOW_SPEED_LIMIT, 100);
			code = curl_easy_setopt(m_pCURL, CURLOPT_LOW_SPEED_TIME, 20);
			// use custom progress callback.
			//code = curl_easy_setopt(m_pCURL, CURLOPT_NOPROGRESS, 0);
			//code = curl_easy_setopt(m_pCURL, CURLOPT_PROGRESSFUNCTION, tHTTPDownloadItem::ProgressCallback);
			//code = curl_easy_setopt(m_pCURL, CURLOPT_PROGRESSDATA, this);
			// use custom header reader.
			//code = curl_easy_setopt(m_pCURL, CURLOPT_HEADERFUNCTION, tHTTPDownloadItem::ReceiveHeaderData);
			//code = curl_easy_setopt(m_pCURL, CURLOPT_HEADERDATA, this);

				
			//curl_easy_setopt(m_pCURL, CURLOPT_PORT, "8080");

			//if (m_Header.length() == 0)
			{
				string RequestURL = WtoUFT8(wURL);
				if (m_Header.length() != 0)
				{
					RequestURL += ("?");
					RequestURL += WtoUFT8(Header);
				}
				code = curl_easy_setopt(m_pCURL, CURLOPT_URL, RequestURL.c_str());
				if (code != CURLE_OK)
					throw HTTPException(HTTP_INIT_FAIL, code);
				code = curl_easy_setopt(m_pCURL, CURLOPT_HTTPGET, true);
				if (code != CURLE_OK)
					throw HTTPException(HTTP_INIT_FAIL, code);
			}
			//else
			//{
			//	code = curl_easy_setopt(m_pCURL, CURLOPT_URL, WtoUFT8(wURL).c_str());
			//	if (code != CURLE_OK)
			//		throw HTTPException(HTTP_INIT_FAIL, code);
			//	string postdata = WtoUFT8(Header);
			//	curl_easy_setopt(m_pCURL, CURLOPT_POSTFIELDS, postdata.c_str());
			//	curl_easy_setopt(m_pCURL, CURLOPT_POSTFIELDSIZE, postdata.length());
			//	curl_easy_setopt(m_pCURL, CURLOPT_POST, true);
			//}
			long  response_code = 0;
			curl_slist *header_list = NULL;
			code = curl_easy_perform(m_pCURL);
			curl_easy_getinfo(m_pCURL, CURLINFO_RESPONSE_CODE, &response_code);

			curl_slist_free_all(header_list);
			curl_easy_setopt(m_pCURL, CURLOPT_HTTPHEADER, NULL);
		}
		catch (...)
		{
			fclose(fp);
			return -1;
		}
		fclose(fp);
		if (m_Notify)
			m_Notify->NotifyEnd(wURL);

		return 0;
	}
	int WinHttpDownload()
	{
		FILE* fp = _wfopen(m_SavePath.c_str(),L"wb");
		if (fp == NULL)
			return -1;

		wstring URL = m_URL;
		wstring Header = m_Header;

		HINTERNET hSession = 0;
		HINTERNET hConnect = 0;
		HINTERNET hRequest = 0;
		CStringW strHeader(L"Content-type: application/x-www-form-urlencoded\r\n");

		// Test data
		CrackedUrl crackedUrl(URL.c_str());

		// Open session.
		hSession = OpenSession();
		if (hSession == NULL) {
			printf("Error:Open session!\n");
			return -1;
		}

		// Connect.
		hConnect = Connect(hSession, crackedUrl.GetHostName(), crackedUrl.GetPort());
		if (hConnect == NULL) {
			printf("Error:Connect failed!\n");
			return -1;
		}

		// Open request.
		hRequest = OpenRequest(hConnect, L"POST", crackedUrl.GetPath(), crackedUrl.GetScheme());
		if (hRequest == NULL) {
			printf("Error:OpenRequest failed!\n");
			return -1;
		}
		// Add request header.
		if (!AddRequestHeaders(hRequest, strHeader)) {
			 printf("Error:AddRequestHeaders failed!\n");
				return -1;
			}

		string path = WtoUFT8(Header);

		CStringA strPostData = path.c_str();
		if (strPostData.GetLength() == 0)
			SendRequest(hRequest, NULL,0);
		// Send post data.
		else if (!SendRequest(hRequest, strPostData,strPostData.GetLength())) {
			printf("Error:SendRequest failed!\n");
			return -1;
		}

		// End request
		if (!EndRequest(hRequest)) {
			printf("Error:EndRequest failed!\n");
			return -1;
		}

		char szBuf[BUF_SIZE+1];
		DWORD dwSize = 0;
		szBuf[0] = 0;

		// Query header info.
	#ifdef USE_WINHTTP
		int contextLengthId = WINHTTP_QUERY_CONTENT_LENGTH;
		int statusCodeId = WINHTTP_QUERY_STATUS_CODE;
		int statusTextId = WINHTTP_QUERY_STATUS_TEXT;
	#else
		int contextLengthId = HTTP_QUERY_CONTENT_LENGTH;
		int statusCodeId = HTTP_QUERY_STATUS_CODE;
		int statusTextId = HTTP_QUERY_STATUS_TEXT;
	#endif
		dwSize = BUF_SIZE;
		if (QueryInfo(hRequest, contextLengthId, szBuf, &dwSize)) {
			szBuf[dwSize] = 0;
			printf("Content length:[%s]\n", szBuf);
		}

		dwSize = BUF_SIZE;
		if (QueryInfo(hRequest, statusCodeId, szBuf, &dwSize)) {
			szBuf[dwSize] = 0;
			printf("Status code:[%s]\n", szBuf);
		}

		dwSize = BUF_SIZE;
		if (QueryInfo(hRequest, statusTextId, szBuf, &dwSize)) {
			szBuf[dwSize] = 0;
			printf("Status text:[%s]\n", szBuf);
		}

		DWORD dwTotalSize = 0;
		// read data.
		for (;;) {
			dwSize = BUF_SIZE;
			if (ReadData(hRequest, szBuf, dwSize, &dwSize) == FALSE) {
				break;
			}

			if (dwSize <= 0) {
				break;
			}
			dwTotalSize += dwSize;

			//szBuf[dwSize] = 0;
			//printf("%s\n", szBuf);    //Output value = value1 + value2
			fwrite(szBuf,1,dwSize,fp);
		}

		CloseInternetHandle(hRequest);
		CloseInternetHandle(hConnect);
		CloseInternetHandle(hSession);
		fclose(fp);

		if (m_Notify)
			m_Notify->NotifyEnd(URL);
		return 0;
	}
	string WtoUFT8(wstring str)
	{
		int length = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, NULL, 0, NULL, FALSE);
		char * str_v = new char[length+10];
		WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, str_v, length, NULL, FALSE);
		string ret_str = str_v;
		delete[] str_v;
		return ret_str;
	}
	IDownloadNotify*	m_Notify;
	wstring				m_URL;
	wstring				m_SavePath;
	wstring				m_Header;
};
////int _tmain(int argc, _TCHAR* argv[])
////{
////    HINTERNET hSession = 0;
////    HINTERNET hConnect = 0;
////    HINTERNET hRequest = 0;
////    CStringW strHeader(L"Content-type: application/x-www-form-urlencoded\r\n");
////
////    // Test data
////    CrackedUrl crackedUrl(L"http://www.easy-creator.net/test2/add.asp");
////    CStringA strPostData("value1=10&value2=14");
////
////	// Open session.
////    hSession = OpenSession(L"HttpPost by l_zhaohui@163.com");
////    if (hSession == NULL) {
////        printf("Error:Open session!\n");
////        return -1;
////    }
////
////    // Connect.
////231    hConnect = Connect(hSession, crackedUrl.GetHostName(), crackedUrl.GetPort());
////232    if (hConnect == NULL) {
////233        printf("Error:Connect failed!\n");
////234        return -1;
////235    }
////236
////237    // Open request.
////238    hRequest = OpenRequest(hConnect, L"POST", crackedUrl.GetPath(), crackedUrl.GetScheme());
////239    if (hRequest == NULL) {
////240        printf("Error:OpenRequest failed!\n");
////241        return -1;
////242    }
////243
////244    // Add request header.
////245    if (!AddRequestHeaders(hRequest, strHeader)) {
////246        printf("Error:AddRequestHeaders failed!\n");
////247        return -1;
////248    }
////249
////250    // Send post data.
////251    if (!SendRequest(hRequest, (const char*)strPostData, strPostData.GetLength())) {
////252        printf("Error:SendRequest failed!\n");
////253        return -1;
////254    }
////255
////256    // End request
////257    if (!EndRequest(hRequest)) {
////258        printf("Error:EndRequest failed!\n");
////259        return -1;
////260    }
////261
////262    char szBuf[BUF_SIZE];
////263    DWORD dwSize = 0;
////264    szBuf[0] = 0;
////265
////266    // Query header info.
////267#ifdef USE_WINHTTP
////268    int contextLengthId = WINHTTP_QUERY_CONTENT_LENGTH;
////269    int statusCodeId = WINHTTP_QUERY_STATUS_CODE;
////270    int statusTextId = WINHTTP_QUERY_STATUS_TEXT;
////271#else
////272    int contextLengthId = HTTP_QUERY_CONTENT_LENGTH;
////273    int statusCodeId = HTTP_QUERY_STATUS_CODE;
////274    int statusTextId = HTTP_QUERY_STATUS_TEXT;
////275#endif
////276    dwSize = BUF_SIZE;
////277    if (QueryInfo(hRequest, contextLengthId, szBuf, &dwSize)) {
////278        szBuf[dwSize] = 0;
////279        printf("Content length:[%s]\n", szBuf);
////280    }
////281
////282    dwSize = BUF_SIZE;
////283    if (QueryInfo(hRequest, statusCodeId, szBuf, &dwSize)) {
////284        szBuf[dwSize] = 0;
////285        printf("Status code:[%s]\n", szBuf);
////286    }
////287
////288    dwSize = BUF_SIZE;
////289    if (QueryInfo(hRequest, statusTextId, szBuf, &dwSize)) {
////290        szBuf[dwSize] = 0;
////291        printf("Status text:[%s]\n", szBuf);
////292    }
////293
////294    // read data.
////295    for (;;) {
////296        dwSize = BUF_SIZE;
////297        if (ReadData(hRequest, szBuf, dwSize, &dwSize) == FALSE) {
////298            break;
////299        }
////300
////301        if (dwSize <= 0) {
////302            break;
////303        }
////304
////305        szBuf[dwSize] = 0;
////306        printf("%s\n", szBuf);    //Output value = value1 + value2
////307    }
////308
////309    CloseInternetHandle(hRequest);
////310    CloseInternetHandle(hConnect);
////311    CloseInternetHandle(hSession);
////312
////313    return 0;
////314}
////315
#endif